package com.geovis.emergency.spd.biz.pc.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.geovis.common.auth.util.AuthUtil;
import com.geovis.emergency.spd.biz.pc.mapper.PcDrillDutyOrgMapper;
import com.geovis.emergency.spd.biz.pc.mapper.PcDrillDutyUserMapper;
import com.geovis.emergency.spd.biz.pc.mapper.PcRollCallMapper;
import com.geovis.emergency.spd.biz.pc.mapper.PcRollCallPersonMapper;
import com.geovis.emergency.spd.biz.pc.service.IPcMessageService;
import com.geovis.emergency.spd.biz.pc.service.IPcRollCallService;
import com.geovis.emergency.spd.cache.SysCache;
import com.geovis.emergency.spd.entity.pc.entity.PcDrillDutyOrg;
import com.geovis.emergency.spd.entity.pc.entity.PcDrillDutyUser;
import com.geovis.emergency.spd.entity.pc.entity.PcRollCall;
import com.geovis.emergency.spd.entity.pc.entity.PcRollCallPerson;
import com.geovis.emergency.spd.entity.pc.pojo.constant.PcMessageConstant;
import com.geovis.emergency.spd.entity.pc.pojo.constant.RollCallConstant;
import com.geovis.emergency.spd.entity.pc.pojo.enums.RollCallStatusEnum;
import com.geovis.emergency.spd.entity.pc.pojo.enums.StatusEnum;
import com.geovis.emergency.spd.entity.pc.pojo.vo.DutyTreeVO;
import com.geovis.emergency.spd.entity.pc.pojo.vo.RollCallUserVO;
import com.geovis.emergency.spd.entity.pc.pojo.vo.TreeVO;
import com.geovis.emergency.spd.redis.RedisUtil;
import com.geovis.emergency.spd.util.MsgUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * <p>
 * 应急预案指挥模块-点名 服务实现类
 * </p>
 *
 * @author lonsho
 * @since 2022-06-28
*/

@Service
@RequiredArgsConstructor
@Slf4j
public class PcRollCallServiceImpl extends ServiceImpl<PcRollCallMapper, PcRollCall> implements IPcRollCallService {

    private final PcRollCallPersonMapper pcRollCallPersonMapper;
    private final IPcMessageService pcMessageService;
    private final PcRollCallMapper pcRollCallMapper;
    private final PcDrillDutyUserMapper pcDrillDutyUserMapper;
    private final PcDrillDutyOrgMapper pcDrillDutyOrgMapper;

    @Override
    public RollCallUserVO callRollDutyUser(String drillId) {

        List<PcDrillDutyOrg> dutyOrgList = pcDrillDutyOrgMapper.selectList(
                Wrappers.lambdaQuery(PcDrillDutyOrg.class)
                        .eq(PcDrillDutyOrg::getPcDrillId, drillId)
        );
        List<PcDrillDutyOrg> collect = dutyOrgList.stream().filter(p -> StatusEnum.FALSE.getCode().equals(p.getParentId())).collect(Collectors.toList());
        Map<PcDrillDutyOrg,List<PcDrillDutyUser>> stringListMap = Maps.newHashMap();
        //组
        for (PcDrillDutyOrg pcDrillDutyOrg : collect) {
            //组下面所有的架构
            List<PcDrillDutyOrg> groupList = dutyOrgList.stream().filter(s -> s.getAncestors().contains(pcDrillDutyOrg.getId())).collect(Collectors.toList());
            Set<String> idList = groupList.stream().map(PcDrillDutyOrg::getId).collect(Collectors.toSet());
            idList.add(pcDrillDutyOrg.getId());
            //获取组所有的用户
            List<PcDrillDutyUser> list = pcDrillDutyUserMapper.selectList(
                    Wrappers.<PcDrillDutyUser>lambdaQuery()
                            .isNotNull(PcDrillDutyUser::getPrincipalIds)
                            .in(PcDrillDutyUser::getPcDrillOrgId,idList)
                            .eq(PcDrillDutyUser::getPcDrillId, drillId)
            );
            //没有用户的组不显示
            if (CollUtil.isNotEmpty(list)){
                stringListMap.put(pcDrillDutyOrg,list);
            }
        }

        PcRollCall pcRollCall = pcRollCallMapper.selectOne(
                Wrappers.lambdaQuery(PcRollCall.class)
                        .eq(PcRollCall::getPcDrillId, drillId)
                        .orderByDesc(PcRollCall::getCreateTime)
                        .last("limit 1")
        );
        //获取最后一次发起的点名记录
        List<PcRollCallPerson> pcRollCallPersonList = Lists.newArrayList();
        if (ObjectUtil.isNotNull(pcRollCall)) {
            pcRollCallPersonList = pcRollCallPersonMapper.selectList(
                    Wrappers.lambdaQuery(PcRollCallPerson.class)
                            .eq(PcRollCallPerson::getPcDrillId, drillId)
                            .eq(PcRollCallPerson::getPcRollCallId, pcRollCall.getId())
            );
        }
        //点名
        List<TreeVO> resultList = Lists.newArrayList();
        for (Map.Entry<PcDrillDutyOrg, List<PcDrillDutyUser>> entry : stringListMap.entrySet()) {
            TreeVO treeVO = new TreeVO();
            treeVO.setName(entry.getKey().getOrgName());
            treeVO.setId(entry.getKey().getId());
            AtomicInteger integer = new AtomicInteger(0);
            for (PcDrillDutyUser pcDrillDutyUser : entry.getValue()) {
                List<String> principalIdList = StrUtil.split(pcDrillDutyUser.getPrincipalIds(), ",");
                List<String> principalNameList = StrUtil.split(pcDrillDutyUser.getPrincipalNames(), ",");
                for (int i = 0; i < principalIdList.size(); i++) {
                    DutyTreeVO childVO = new DutyTreeVO();
                    childVO.setId(principalIdList.get(i));
                    childVO.setName(principalNameList.get(i));
                    childVO.setParentName(entry.getKey().getOrgName());
                    childVO.setParentId(entry.getKey().getId());
                    Optional<PcDrillDutyOrg> firstOrg = dutyOrgList.stream().filter(s -> s.getId().equals(pcDrillDutyUser.getPcDrillOrgId())).findFirst();
                    if (firstOrg.isPresent()){
                        childVO.setPostId(firstOrg.get().getId());
                        childVO.setPostName(firstOrg.get().getOrgName());
                    }
                    //查找是否有点名记录
                    int finalI = i;
                    Optional<PcRollCallPerson> first = pcRollCallPersonList.stream().filter(s -> s.getPersonId().equals(principalIdList.get(finalI))).findFirst();
                    if (first.isPresent() && RollCallStatusEnum.CONFIRMED.getCode().equals(first.get().getStatus())) {
                        childVO.setStatus(RollCallStatusEnum.CONFIRMED.getCode());
                        integer.getAndIncrement();
                    } else {
                        childVO.setStatus(RollCallStatusEnum.UNCONFIRMED.getCode());
                    }
                    treeVO.getChildren().add(childVO);
                    treeVO.setHasChildren(true);
                }

            }
            treeVO.setExtra(StrUtil.format("{}/{}", integer.get(), entry.getValue().size()));
            resultList.add(treeVO);
        }
        RollCallUserVO rollCallUserVO = new RollCallUserVO();
        if (ObjectUtil.isNotNull(pcRollCall)) {
            rollCallUserVO.setStatus(pcRollCall.getStatus());
        }
        String paramValue = SysCache.getParamValue(RollCallConstant.CALL_ROLL_TIME_PARAM);
        Integer rollCallTime = RollCallConstant.DEFAULT_COUNT_DOWN;
        if (StrUtil.isNotBlank(paramValue)) {
            rollCallTime = Convert.toInt(paramValue, rollCallTime / 1000) * 1000;
        }
        if (ObjectUtil.isNotNull(pcRollCall)) {
            rollCallTime = RedisUtil.get(StrUtil.format(RollCallConstant.CALL_ROLL_TIME, pcRollCall.getId()), rollCallTime / 1000);
            rollCallUserVO.setCallRollTime(rollCallTime * 1000);
        } else {
            rollCallUserVO.setCallRollTime(rollCallTime);
        }

        if (ObjectUtil.isNotNull(pcRollCall)) {
            rollCallUserVO.setRollCallId(pcRollCall.getId());
        }
        rollCallUserVO.setUserList(resultList);

        return rollCallUserVO;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void startPcRollCall(String drillId) {
        PcRollCall pcRollCall = new PcRollCall();
        pcRollCall.setStatus(RollCallStatusEnum.UNCONFIRMED.getCode());
        pcRollCall.setPcDrillId(drillId);
        pcRollCall.setCreateUser(AuthUtil.getCurrentUserId());
        baseMapper.insert(pcRollCall);

        Set<String> userList = CollUtil.set(false);

        List<PcDrillDutyUser> dutyUserList = pcDrillDutyUserMapper.selectList(
                Wrappers.lambdaQuery(PcDrillDutyUser.class)
                        .isNotNull(PcDrillDutyUser::getPrincipalIds)
                        .eq(PcDrillDutyUser::getPcDrillId,drillId)
        );
        Assert.notEmpty(dutyUserList, "发起失败，参与人员为空");


        String paramValue = SysCache.getParamValue(RollCallConstant.CALL_ROLL_TIME_PARAM);
        Integer rollCallTime = RollCallConstant.DEFAULT_COUNT_DOWN;
        if (StrUtil.isNotBlank(paramValue)){
            rollCallTime = Convert.toInt(paramValue,rollCallTime / 1000);
        }
        for (PcDrillDutyUser pcDrillDutyUser : dutyUserList) {
            List<String> principalIdList = StrUtil.split(pcDrillDutyUser.getPrincipalIds(), ",");
            for (String userId : principalIdList) {
                PcRollCallPerson rollCallPerson = new PcRollCallPerson();
                rollCallPerson.setPersonId(userId);
                rollCallPerson.setPcRollCallId(pcRollCall.getId());
                rollCallPerson.setPcDrillId(drillId);
                rollCallPerson.setStatus(RollCallStatusEnum.UNCONFIRMED.getCode());
                rollCallPerson.setBasicDutyPostId(pcDrillDutyUser.getPcDrillOrgId());
                pcRollCallPersonMapper.insert(rollCallPerson);
                MsgUtil.sendRollCallNotice(drillId,pcRollCall.getId(), userId,rollCallTime * 1000);
            }
        }

        MsgUtil.saveMsg(PcMessageConstant.TYPE_START_ROLL_CALL_MSG, "发起一键点名", AuthUtil.getCurrUser().getRealName() + "发起了一键点名", pcRollCall.getPcDrillId(), pcRollCall.getId());
        MsgUtil.startRollCall(pcRollCall.getPcDrillId());
        ThreadUtil.execute(getRunnable(rollCallTime,pcRollCall.getPcDrillId(),pcRollCall.getId()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void stopPcRollCall(String rollCallId,String showMsg) {
        PcRollCall pcRollCall = baseMapper.selectById(rollCallId);
        if (RollCallStatusEnum.UNCONFIRMED.getCode().equals(pcRollCall.getStatus())){
            pcRollCall.setStatus(RollCallStatusEnum.CONFIRMED.getCode());
            baseMapper.updateById(pcRollCall);
            if(StatusEnum.TRUE.getCode().equals(showMsg)){
                MsgUtil.saveMsg(PcMessageConstant.TYPE_STOP_ROLL_CALL_MSG, "点名已结束", AuthUtil.getCurrUser().getRealName() + "结束了点名", pcRollCall.getPcDrillId(), rollCallId);
                MsgUtil.stopRollCallNotice(pcRollCall.getPcDrillId());
            }else {
                MsgUtil.saveMsg(PcMessageConstant.TYPE_STOP_ROLL_CALL_MSG, "点名已结束",  "时间到了，点名结束", pcRollCall.getPcDrillId(), rollCallId);
                MsgUtil.stopRollCallNotice(pcRollCall.getPcDrillId());
            }
            RedisUtil.del(StrUtil.format(RollCallConstant.CALL_ROLL_TIME,pcRollCall.getId()));
        }

    }
    private Runnable getRunnable(int time ,String pcDrillId,String rollCallId){
        return () -> {
            RedisUtil.set(StrUtil.format(RollCallConstant.CALL_ROLL_TIME,rollCallId),time);
            ThreadUtil.sleep(1000); //后台一秒偏差时间
            for (int i = time; i >= 0;i--){
                log.info("点名倒计时 {}s",i);
                if (i == 0){
                    //时间到了，停止点名
                    RedisUtil.del(StrUtil.format(RollCallConstant.CALL_ROLL_TIME,rollCallId));
                    MsgUtil.saveMsg(PcMessageConstant.TYPE_STOP_ROLL_CALL_MSG, "点名已结束",  "时间到了，点名结束",pcDrillId, rollCallId);
                    stopPcRollCall(rollCallId,StatusEnum.FALSE.getCode());
                }else {
                    //如果缓存里面没有了时间记录，说明手动停止了点名
                    Integer currentTime = RedisUtil.get(StrUtil.format(RollCallConstant.CALL_ROLL_TIME, rollCallId));
                    if (null == currentTime){
                        break;
                    }
                    RedisUtil.set(StrUtil.format(RollCallConstant.CALL_ROLL_TIME,rollCallId),i);
                    ThreadUtil.sleep(1000);
                }
            }
        };
    }
}
